#include "string_util.h"
#include "../FileSys.h"
#include <fstream>
#include <QFile>
#include <QFileInfo>
#include <QDir>
#include <QDataStream>
#include <QUuid>

#ifndef WIN32
#define _countof(x) (sizeof(x) / sizeof((x)[0]))
#endif

namespace qb{
	namespace base{
		namespace stringutil{
			enum{
				MAX_CHAR_SIZE = 256,
			};
		}

#define TOLOWERCHAR(C) 		((C>='A' && C<='Z')?(C+('a'-'A')):C)

		int strrfind(const char* str,int ipos,char c)
		{
			for (int i=ipos;i>=0;--i)
				if(str[i]==c)
					return i;

			return -1;
		}
		static char inline GetCaseChar(bool igoreCase,const char& c)
		{
			return (!igoreCase && (c>='A' && c<='Z'))?c+32:c;
		}
		static int findpos(int* intarr,int count,int v)
		{
			for (int i=0;i<count;i++)
			{
				if(intarr[i]==v)return i;
			}
			return -1;
		}

		int StringUtil::SplitString(const char* str, int size, const BaseString& key, std::vector<BaseString>& strings)
		{
			std::vector<strinfo> lineinfos;
			SplitStringInfo(str, size, key, lineinfos);
			for (auto it = lineinfos.begin(); it != lineinfos.end(); ++it)
			{
				if (it->len <= 0)continue;
				strings.push_back(BaseString(str + it->pos, it->len));
			}
			return strings.size();
		}

		int StringUtil::SplitStringInfo(const char* str, int size, const BaseString& key, strinfo* strings, int capcity)
		{
			int start = 0;
			int ipos = 0;
			for (int i = 0; i < size; ++i)
			{
				if (key.find(str[i]) != BaseString::npos)
				{
					if (i > start && ipos < capcity)
					{
						strinfo info; info.pos = start; info.len = i - start;
						strings[ipos++] = info;
					}
					start = i + 1;
				}
			}
			if (size > start && ipos < capcity)
			{
				strinfo info; info.pos = start; info.len = size - start;
				strings[ipos++] = info;
			}
			return ipos;
		}
		
		int StringUtil::SplitStringInfo(const char* str, int size, const BaseString& key, std::vector<strinfo>& strings)
		{
			int start = 0;
			for (int i = 0; i < size; ++i)
			{
				if (key.find(str[i]) != BaseString::npos)
				{
					if (i >= start)
					{
						strinfo info; info.pos = start; info.len = i - start;
						strings.push_back(info);
					}
					start = i + 1;
				}
			}
			if (size > start)
			{
				strinfo info; info.pos = start; info.len = size - start;
				strings.push_back(info);
			}
			return strings.size();
		}
		
		int StringUtil::SplitStringInfo(const char* str, int size, const BaseString& key, SimpleArray<strinfo>& strings)
		{
			int start = 0;
			for (int i = 0; i < size; ++i)
			{
				if (key.find(str[i]) != BaseString::npos)
				{
					if (i >= start)
					{
						strinfo info; info.pos = start; info.len = i - start;
						strings.push_back(info);
					}
					start = i + 1;
				}
			}
			if (size > start)
			{
				strinfo info; info.pos = start; info.len = size - start;
				strings.push_back(info);
			}
			return strings.size();
		}

		int StringUtil::SplitStringInfo(const wchar_t* str, int size, const WideString& key, strinfo* strings, int capcity)
		{
			int start = 0;
			int ipos = 0;
			for (int i = 0; i < size; ++i)
			{
				if (key.find(str[i]) != WideString::npos)
				{
					if (i > start && ipos < capcity)
					{
						strinfo info; info.pos = start; info.len = i - start;
						strings[ipos++] = info;
					}
					start = i + 1;
				}
			}
			if (size > start && ipos < capcity)
			{
				strinfo info; info.pos = start; info.len = size - start;
				strings[ipos++] = info;
			}
			return ipos;
		}

		int StringUtil::SplitStringInfoEx(const char* str, int size, const BaseString& key, std::vector<strinfo>& tokens, bool ignoreZeroLength /*= true*/)
		{
			int start = 0;
			for (int i = 0; i < size; ++i)
			{
				if (key.find(str[i]) != BaseString::npos)
				{
					if ((ignoreZeroLength && i > start) || (!ignoreZeroLength && i >= start))
					{
						strinfo info; info.pos = start; info.len = i - start;
						tokens.push_back(info);
					}
					start = i + 1;
				}
			}
			if ((ignoreZeroLength && size > start) || (!ignoreZeroLength && size >= start))
			{
				strinfo info; info.pos = start; info.len = size - start;
				tokens.push_back(info);
			}
			return tokens.size();
		}

        void StringUtil::SplitFileNameEx(const BaseString& full, BaseString& path, BaseString& base, BaseString& ext)
        {
            int ipos = full.rfind('/');
            ipos = ipos < 0 ? full.rfind('\\'):ipos;
            if (ipos >= 0)
            {
                path = full.substr(0, ipos);
                BaseString name = full.substr(ipos + 1, full.size() - ipos - 1);
                int dotpos = name.rfind('.');
                if (dotpos >= 0)
                {
                    base = name.substr(0, dotpos);
                    ext = name.substr(dotpos + 1, name.size() - 1 - dotpos);
                }
                else
                {
                    base = name;
                    ext = "";
                }
            }
        }

        void StringUtil::SplitFileName(const BaseString& full, BaseString& path, BaseString& name)
        {
            int iend = -1;
            int i = full.size() - 1;
            for (; i >= 0; i--)
            {
                bool isslash = full[i] == '/' || full[i] == '\\';
                if (iend < 0)
                {
                    if (isslash)
                        iend = i;
                }
                else
                {
                    if (!isslash)
                    {
                        i++;
                        break;
                    }
                }
            }
            if (i >= 0 && i <= iend && iend >= 0)
            {
                path = full.substr(0, i);
                name = full.substr(iend + 1, full.size() - iend - 1);
            }
        }

        int  StringUtil::FindInSet(const char* str,int len,BaseString strset)
        {
            for (int i=len-1;i>=0;i--)
            {
                if(strset.find(str[i])!=BaseString::npos)
                    return i;
            }
            return -1;
        }

		char* StringUtil::LoadFileContent(const wchar_t* fileName, int& fileSize)
		{
			fileSize = 0;
			QString filePath = QString::fromWCharArray(fileName);
			QFile file(filePath);
			if(file.open(QIODevice::ReadOnly))
				return nullptr;

			fileSize = file.size();
			QDataStream ds(&file);

			char* data = new char[fileSize];
			memset(data, 0, fileSize);
			ds.readRawData(data, fileSize);
			file.close();
			return data;
		}

		int StringUtil::LoadLinesFromFile(const wchar_t* fileName, std::vector<BaseString>& lines, const BaseString& key /*= "\r\n"*/, bool skipBlankLines /*= true*/)
		{
			int fsize = 0;
			char* buf = LoadFileContent(fileName, fsize);
			if (buf == NULL || fsize <= 0)
				return 0;
			if (skipBlankLines)
				SplitString(buf, fsize, key, lines);
			else
			{
				std::vector<strinfo> lineinfos;
				SplitStringInfoEx(buf, fsize, key, lineinfos, skipBlankLines);
				int zero_index = 0;
				for (auto it = lineinfos.begin(); it != lineinfos.end(); ++it)
				{
					strinfo& si = *it;
					zero_index = si.len <= 0 ? (zero_index + 1) : 0;
					if (zero_index % 2 == 1)continue;
					lines.push_back(BaseString(buf + si.pos, si.len));
				}
			}
			delete[] buf;
			return lines.size();
		}

        int strrfind2(const char* str,int ipos,char c)
        {
            for (int i=ipos;i>=0;--i)
                if(str[i]==c)
                    return i;

            return -1;
        }

        const char* StringUtil::GetShortFileName( const char* fileName,int count )
        {
            if(fileName==NULL)
                return NULL;
            int len = strlen(fileName);
            int ipos= len-1;

            int num = 0;
            while (num<count)
            {
                int newpos = strrfind2(fileName,ipos,'\\');
                if(newpos<0)
                    break;
                ipos = newpos-1;
                num++;
            }
            if(num<=0)
                return fileName;

            const char* short_name = fileName+ipos+1;
            return short_name;
        }

        template<class Container>
        bool StringUtil::WriteLinesImpl(const Container& lines,const BaseString& fileName,bool append)
        {
            QFile* file;
            FileSys::instance()->createFile(fileName.c_str(), QIODevice::ReadWrite, file);
            if(file == nullptr)
                return false;

            int fsize = 0;
            for (typename Container::const_iterator it = lines.begin();it!=lines.end();++it)
            {
                fsize	+= it->size()+2;
            }
            if(append)fsize+=2;
            char* buf	= new char[fsize+1];
            char* ptr	= buf;
            memset(buf,0,fsize+1);
            if(append)
            {
                //*ptr++	= '\r';
                *ptr++	= '\n';
            }
            for (typename Container::const_iterator it = lines.begin();it!=lines.end();++it)
            {
                memcpy(ptr,it->c_str(),it->size());
                ptr		+= it->size();
                //*ptr++	= '\r';
                *ptr++	= '\n';
            }

            if(append)
            {
                file->seek(file->size());
            }
            else
            {
                file->seek(0);
            }

            file->write(buf, fsize);
            FileSys::instance()->closeFile(file);

            return true;
        }
        bool StringUtil::WriteLines(const SimpleArray<BaseString>& lines,const BaseString& fileName,bool append)
        {
            return WriteLinesImpl<SimpleArray<BaseString>>(lines,fileName,append);
        }
        bool StringUtil::WriteLines(const std::vector<BaseString>& lines,const BaseString& fileName,bool append)
        {
            return WriteLinesImpl<std::vector<BaseString>>(lines,fileName,append);
        }
        bool StringUtil::WriteLines(const std::set<BaseString>& lines,const BaseString& fileName,bool append)
        {
            return WriteLinesImpl<std::set<BaseString>>(lines,fileName,append);
        }

        bool StringUtil::IsNumber(const BaseString& str)
        {
            return IsNumber(str.c_str(),str.size());
        }

        bool StringUtil::IsNumber(const char* str,int len)
        {
            char c = 0;
            for (int i=0;i<len;i++)
            {
                c = str[i];
                if((c<'0' || c>'9') && c!='.')
                    return false;
            }
            return true;
        }

        void StringUtil::tohex(unsigned char c,char str[])
        {
            static char codes[]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
            str[0]=codes[c>>4];
            str[1]=codes[c&0x0f];
        }

        int StringUtil::tohex(wchar_t c, char* str)
        {
            static char codes[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
            unsigned char low = c & 0x00ff;
            unsigned char hi = (c & 0xff00) >> 8;
            int i = 0;
            str[i++] = '0'; str[i++] = 'x';
            if (hi != 0)
            {
                str[i++] = codes[hi >> 4];
                str[i++] = codes[hi & 0x0f];
            }
            str[i++] = codes[low >> 4];
            str[i++] = codes[low & 0x0f];
            return i;
        }

        QString StringUtil::GenerateGuidString()
        {
            QUuid id = QUuid::createUuid();
            return id.toString();
        }
		bool StringUtil::SaveFileContent(const char* buf, int size, const wchar_t* fileName)
		{
			QString strPath = QString::fromWCharArray(fileName);
			QFileInfo fileInfo(strPath);
			if (!fileInfo.absoluteDir().mkpath(fileInfo.absolutePath()))
				return false;

			QFile file(strPath);
			if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate))
				return false;
			file.write(buf, size);
			file.close();
			return true;
		}

		int StringUtil::GetStringRealBytes(const std::string& str)
		{
			int bytes = str.size() + 1;
			return bytes;
		}

		static int CalcCapcityBySize(int size) {
			// 最大处理1mb的尺寸
			static int capcitys[] = {
				15, 31, 47, 70, 105, 157,235, 352, 528, 792, 1188, 1782,
				2673, 4009, 6013, 9019, 13528, 20292,30438, 45657, 68485, 102727,
				154090, 231135, 346702, 520053, 780079, 1170118
			};
			for (int i = 0; i < _countof(capcitys); i++)
			{
				if (size <= capcitys[i])
					return capcitys[i];
			}
			return size;
		}
		int StringUtil::GetStringAllBytes(const std::string& str)
		{
			int bytes = sizeof(std::string);
			const char* base = (const char*)&str;
			if (str.c_str() < base || str.c_str() >= base + bytes)
			{
				// 考虑到对齐,实际占用还要多
				/*	capcity     15 == > 31  size:    16
					capcity     31 == > 47  size:    32
					capcity     47 == > 70  size:    48
					capcity     70 == > 105  size:    71
					capcity    105 == > 157  size:   106
					capcity    157 == > 235  size:   158
					capcity    235 == > 352  size:   236
					capcity    352 == > 528  size:   353
					capcity    528 == > 792  size:   529
					capcity    792 == > 1188  size:   793
					0-15	==>	16
					16-31	==>	31
					32-47	==>	47
					48-70	==> 70
					71-105	==>	105
				*/

				bytes += CalcCapcityBySize(str.size()) + 1;
			}
			return bytes;
		}

		void StringUtil::StringCopy(char* dest, int capcity, const char* source)
		{
			int i = 0;
			if (dest && source && capcity > 0)
			{
				while (*source && i + 1 < capcity)
				{
					dest[i++] = *source++;
				}
				dest[i] = 0;
				dest[capcity - 1] = 0;
			}
		}

	}//namespace base;
}//namespace ss;
